package com.zxd.interview.util.holidays;

import com.zxd.interview.util.date.DateHelper;
import com.zxd.interview.util.PreconditionUtils;

import java.time.LocalDate;
import java.util.*;

/**
 * 工作日判断的帮助类
 */
public abstract class WorkDateUtils {


    private WorkDateUtils() {
        throw new AssertionError("工具类不允许实例化");
    }


    /**
     * 计算工作日的时间差
     *
     * @param preDate  前一个日期
     * @param nextDate 后一个日期
     */
    public static long dateDifference(Date preDate, Date nextDate) {
        return timeDifference(preDate.getTime(), nextDate.getTime());
    }

    public static long timeDifference(long preTime, long nextTime) {
        Calendar calendar = Calendar.getInstance();

        // 1. 保证 前一个日期 大于 后一个日期
        PreconditionUtils.checkArgument(preTime <= nextTime,"前一个日期" + DateHelper.getString(preTime, DateHelper.yyyyMMdd_hhmmssSSS) + "不能大于后一个日期" + DateHelper.getString(nextTime, DateHelper.yyyyMMdd_hhmmssSSS));
        // 2. 校准 preTime, 调整到明天0点整点
        long timeDiff = 0;
        calendar.setTimeInMillis(preTime);
        calendar.add(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        // 2.1. 如果是节假日调休或工作日, 就记录校准时间差
        if (isWorkTime(preTime)) {
            timeDiff += calendar.getTimeInMillis() - preTime;
        }
        preTime = calendar.getTimeInMillis();

        // 3. 校准 nextTime, 调整到当天0点整点
        calendar.setTimeInMillis(nextTime);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        // 3.1. 如果是节假日调休或工作日, 就记录校准时间差
        if (isWorkTime(nextTime)) {
            timeDiff += nextTime - calendar.getTimeInMillis();
        }
        nextTime = calendar.getTimeInMillis();

        // 4. 模拟时间流逝, 计算在工作日内的时间差
        while (preTime < nextTime) {
            // 3. 如果是节假日调休或工作日, 就记录校准时间差
            if (isWorkTime(preTime)) {
                timeDiff += DateHelper.DAY_TIME;
            }
            preTime += DateHelper.DAY_TIME;
        }

        return timeDiff;
    }

    /**
     * 判断是否为 工作日, 节假日调休或正常工作日
     *
     * @param time 计算的日期
     */
    public static boolean isWorkTime(long time) {
        String day = DateHelper.getString(time, DateHelper.yyyyMMdd);
        //判断当前日期是否为工作日
        HolidaysConfig holidaysConfig = HolidaysConfig.getInstance();
        //处在假期调休的工作日，需要进行提醒
        Map<String, String> workingdaysMap = holidaysConfig.getWorkingdaysMap();
        //处在工作日中的节假期。不需要进行提醒
        Map<String, String> holidaysMap = holidaysConfig.getHolidaysMap();
        if (Objects.isNull(workingdaysMap) || workingdaysMap.isEmpty() || Objects.isNull(holidaysMap) || holidaysMap.isEmpty()) {
            throw new RuntimeException("获取 holidays.xml 配置失败! 无法判断是否为工作日!");
        }
        // 处在假期调休的工作日
        if (workingdaysMap.containsKey(day)) {
            return true;
        }
        // 处在工作日中的节假期
        if (holidaysMap.containsKey(day)) {
            return false;
        }
        // 3. 是周末返回false
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        if (dayOfWeek == Calendar.SUNDAY || dayOfWeek == Calendar.SATURDAY) {
            return false;
        }
        // 4. 是正常工作日就返回true
        return true;
    }

    public static boolean isWorkDate(Date date) {
        return isWorkTime(date.getTime());
    }

    /**
     * 获取某个时间段内的工作日
     *
     * @param beginDate 开始时间
     * @param endDate   结束时间
     * @return
     */
    public static List<String> getWorkDate(Date beginDate, Date endDate) {
        long days = DateHelper.countDay(beginDate, endDate);
        if (days < 1) {
            return null;
        }
        List<String> everyDays = new ArrayList<>();
        if (isWorkDate(beginDate)) {
            everyDays.add(DateHelper.getString(beginDate, DateHelper.yyyyMMdd));
        }
        LocalDate beforeLocalDate = DateHelper.date2LocalDate(beginDate);
        for (int i = 1; i <= days; i++) {
            LocalDate currLocalDate = beforeLocalDate.plusDays(i);
            if (isWorkDate(DateHelper.localDate2Date(currLocalDate))) {
                everyDays.add(currLocalDate.toString());
            }
        }
        return everyDays;
    }

    /**
     * 获取未来第 past 天的工作日
     *
     * @param past 未来多少天
     * @param isContainToday 是否包含今天
     * @return
     */
    public static List<String> getFetureWorkDate(int past,boolean isContainToday) {
        List<String> everyDays = new ArrayList<>();
        LocalDate currLocalDate = LocalDate.now();
        // 如果包含今天，则需要从今天开始计算
        if(isContainToday){
            if (isWorkDate(DateHelper.localDate2Date(currLocalDate))) {
                everyDays.add(currLocalDate.toString());
                past = past -1;
            }
        }
        int i = 1;
        int count = 0;
        while (count < past) {
            LocalDate localDateTemp = currLocalDate.plusDays(i++);
            if (isWorkDate(DateHelper.localDate2Date(localDateTemp))) {
                everyDays.add(localDateTemp.toString());
                count++;
            }
        }
        return everyDays;
    }


}
